typedef class sram_model;

class sram_model_callbacks extends vmm_xactor_callbacks;
   // Lab 2c - Create 2 callback methods pre_trans() and post_trans()
   virtual task pre_trans(sram_model bfm, sram_trans tr, ref bit drop); endtask
   virtual task post_trans(sram_model bfm, sram_trans tr); endtask
endclass

class sram_model extends vmm_xactor;
   `vmm_typename(sram_model)
   virtual sram_if.memprt  sigs;
   logic [31:0]            memQ[int];
   string                  resp_kind;
   bit [7:0]               start_addr, end_addr;

   
   extern function new(string inst, vmm_unit parent=null, int port_id);
   extern virtual function void configure_ph();
   extern virtual function void connect_ph();
   extern virtual function void start_of_sim_ph();
   extern virtual protected task main();
endclass

function sram_model::new(string inst, vmm_unit parent=null, int port_id);
   super.new(get_typename(), inst, port_id, parent);
endfunction

task sram_model::main();
   int tr_count = 0;
   sram_trans tr = new();
   super.main();

   tr.stream_id = this.stream_id;
   sigs.axi4_cb.m00_axi_arready <= 1'b1;

   fork
      bit [31:0] addr;
      bit [7:0]  dcnt;
      bit [31:0] val = 0;
      while (1) begin : w0

         this.notify.indicate(vmm_xactor::XACTOR_IDLE);
         this.notify.reset(vmm_xactor::XACTOR_BUSY);
         @(posedge(sigs.axi4_cb.m00_axi_awvalid));         
         sigs.axi4_cb.m00_axi_awready <= 1'b1;
         @(sigs.axi4_cb);
         this.notify.reset(vmm_xactor::XACTOR_IDLE);
         this.notify.indicate(vmm_xactor::XACTOR_BUSY);

         addr = sigs.axi4_cb.m00_axi_awaddr;
         tr.address = addr;
         tr.data_id = tr_count++;

         tr.kind = sram_trans::WRITE;
         @(posedge(sigs.axi4_cb.m00_axi_wvalid));
         sigs.axi4_cb.m00_axi_rlast  <= 1'b0;   
         sigs.axi4_cb.m00_axi_wready <= 1'b1;
         @(sigs.axi4_cb);
         if(addr >= start_addr && addr <= end_addr) begin
            if (memQ.exists(addr)) begin
               memQ[addr] = sigs.axi4_cb.m00_axi_wdata;
               tr.data = memQ[addr];
  	           `vmm_trace(log, tr.psdisplay("sram_model received a WRITE request "));
            end
            else begin
               `vmm_trace(log, tr.psdisplay("sram_model received a abnormal WRITE request "));
            end
         end
         @(sigs.axi4_cb);
         `vmm_callback(sram_model_callbacks, post_trans(this, tr));
      end

      while(1) begin:r0
         this.notify.indicate(vmm_xactor::XACTOR_IDLE);
         this.notify.reset(vmm_xactor::XACTOR_BUSY);
         @(posedge(sigs.axi4_cb.m00_axi_arvalid));         
         sigs.axi4_cb.m00_axi_arready <= 1'b1;
         @(sigs.axi4_cb);
         this.notify.reset(vmm_xactor::XACTOR_IDLE);
         this.notify.indicate(vmm_xactor::XACTOR_BUSY);

         addr = sigs.axi4_cb.m00_axi_araddr;
         tr.address = addr;
         tr.data_id = tr_count++;

         for(dcnt=0;dcnt<16;dcnt++)begin
            sigs.axi4_cb.m00_axi_rdata  <= val++;           
            sigs.axi4_cb.m00_axi_rvalid <= 1'b1;
            if(dcnt==15)sigs.axi4_cb.m00_axi_rlast <= 1'b1; 
            else sigs.axi4_cb.m00_axi_rlast <= 1'b0;      
            @(sigs.axi4_cb);
         end 
         
         sigs.axi4_cb.m00_axi_rlast <= 1'b0;        
         sigs.axi4_cb.m00_axi_rvalid <= 1'b0;       
         @(sigs.axi4_cb);
         `vmm_trace(log, tr.psdisplay("sram_model received a READ request "));
         `vmm_callback(sram_model_callbacks, post_trans(this, tr));
      end
   join

   // Lab 2c - insert callback hook post_trans() after WRITE/READ cycle
  
endtask

function void sram_model::configure_ph();
   `vmm_note(log, $psprintf("sram_model[%0d] - Configurating the sram start and end addresses within the configure_ph()", this.stream_id));
   resp_kind  = "RESP_RAND";
   start_addr = 0;
   end_addr   = '1;
endfunction

function void sram_model::connect_ph();
   `vmm_note(log, $psprintf("sram_model[%0d] - Connecting the sram_model to the DUT within the connect_ph()", this.stream_id)); 
   this.sigs = sram_port.sigs;
endfunction

function void sram_model::start_of_sim_ph();
   if (sigs == null)
      `vmm_fatal(log, "Virtual port not connected to the actual interface instance");
endfunction


